package mysql

import (
	"context"
	_ "github.com/go-sql-driver/mysql"
	"sync"
	"take-a-break/component/config"
	"xorm.io/xorm"
	"xorm.io/xorm/contexts"
	"xorm.io/xorm/names"
)

type Options struct {
	Dns         string
	MaxIdleConn int
	MaxOpenConn int
}

var sessionList = sync.Map{}

var columnMapper names.Mapper

func InitMySQL() {
	nodes := config.GetStringMap("mysql")
	for node := range nodes {
		masterEngine, err := xorm.NewEngine("mysql", config.GetString("mysql."+node+".dsn"))
		slaves := config.GetStringSlice("mysql." + node + ".slaves")
		slaveEngines := make([]*xorm.Engine, len(slaves))
		for i, slave := range slaves {
			engine, err := xorm.NewEngine("mysql", slave)
			if err != nil {
				panic("create slave [" + node + "] connection failed:" + err.Error())
			}
			slaveEngines[i] = engine
		}
		engineGroup, err := xorm.NewEngineGroup(masterEngine, slaveEngines)
		if err != nil {
			panic("mysql node [" + node + "] connection failed: " + err.Error())
		}

		engineGroup.SetMaxIdleConns(config.GetInt("mysql." + node + ".pool.idle"))
		engineGroup.SetMaxOpenConns(config.GetInt("mysql." + node + ".pool.active"))

		if columnMapper != nil {
			engineGroup.SetColumnMapper(columnMapper)
		}
		engineGroup.AddHook(NewTracingHook())
		sessionList.Store(node, engineGroup)
	}
}

func GetSession(args ...string) *xorm.EngineGroup {
	name := "default"
	if len(args) > 0 {
		name = args[0]
	}
	if v, ok := sessionList.Load(name); ok {
		return v.(*xorm.EngineGroup)
	}
	return nil
}

// SetColumnMapper 设置字段映射方案
func SetColumnMapper(mapper names.Mapper) {
	columnMapper = mapper
}

type TracingHook struct {
	// 注意Hook伴随DB实例的生命周期，所以我们不能在Hook里面寄存span变量
	// 否则就会发生并发问题
	before func(c *contexts.ContextHook) (context.Context, error)
	after  func(c *contexts.ContextHook) error
}

// xorm的hook接口需要满足BeforeProcess和AfterProcess函数
func (h *TracingHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
	return h.before(c)
}

func (h *TracingHook) AfterProcess(c *contexts.ContextHook) error {
	return h.after(c)
}

func before(c *contexts.ContextHook) (context.Context, error) {
	// c.ctx = context.WithValue(c.ctx, "mysqlStart", time.Now())
	return c.Ctx, nil
}

func NewTracingHook() *TracingHook {
	return &TracingHook{
		before: before,
	}
}

// 让编译器知道这个是xorm的Hook，防止编译器无法检查到异常
var _ contexts.Hook = &TracingHook{}
